package com.lgmshare.component.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.regex.Pattern;

/**
 * 正则表达式校验
 *
 * @author lim
 * @description: TODO
 * @email lgmshare@gmail.com
 * @datetime 2017/4/19 14:38
 */
public class RegexUtils {

    /**
     * 正则：手机号
     */
    public static final String REGEX_MOBILE = "^[1][3456789]\\d{9}";
    /**
     * 正则：电话号码
     */
    public static final String REGEX_TEL = "^0\\d{2,3}[- ]?\\d{7,8}";
    /**
     * 正则：身份证号码15位
     */
    public static final String REGEX_ID_CARD15 = "^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$";
    /**
     * 正则：身份证号码18位
     */
    public static final String REGEX_ID_CARD18 = "^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9Xx])$";
    /**
     * 正则：邮箱
     */
    public static final String REGEX_EMAIL = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
    /**
     * 正则：URL
     */
    public static final String REGEX_URL = "[a-zA-z]+://[^\\s]*";
    /**
     * 正则：汉字
     */
    public static final String REGEX_ZH = "^[\\u4e00-\\u9fa5]+$";
    /**
     * 正则：用户名，取值范围为a-z,A-Z,0-9,"_",汉字，不能以"_"结尾,用户名必须是6-20位
     */
    public static final String REGEX_USERNAME = "^[\\w\\u4e00-\\u9fa5]{6,20}(?<!_)$";
    /**
     * 正则：yyyy-MM-dd格式的日期校验，已考虑平闰年
     */
    public static final String REGEX_DATE = "^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$";
    /**
     * 正则：IP地址
     */
    public static final String REGEX_IP = "((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)";

    /************** 以下摘自http://tool.oschina.net/regex **************/
    /**
     * 正则：双字节字符(包括汉字在内)
     */
    public static final String REGEX_DOUBLE_BYTE_CHAR = "[^\\x00-\\xff]";
    /**
     * 正则：空白行
     */
    public static final String REGEX_BLANK_LINE = "\\n\\s*\\r";
    /**
     * 正则：QQ号
     */
    public static final String REGEX_TENCENT_NUM = "[1-9][0-9]{4,}";
    /**
     * 正则：中国邮政编码
     */
    public static final String REGEX_ZIP_CODE = "[1-9]\\d{5}(?!\\d)";
    /**
     * 正则：正整数
     */
    public static final String REGEX_POSITIVE_INTEGER = "^[1-9]\\d*$";
    /**
     * 正则：负整数
     */
    public static final String REGEX_NEGATIVE_INTEGER = "^-[1-9]\\d*$";
    /**
     * 正则：整数
     */
    public static final String REGEX_INTEGER = "^-?[1-9]\\d*$";
    /**
     * 正则：非负整数(正整数 + 0)
     */
    public static final String REGEX_NOT_NEGATIVE_INTEGER = "^[1-9]\\d*|0$";
    /**
     * 正则：非正整数（负整数 + 0）
     */
    public static final String REGEX_NOT_POSITIVE_INTEGER = "^-[1-9]\\d*|0$";
    /**
     * 正则：正浮点数
     */
    public static final String REGEX_POSITIVE_FLOAT = "^[1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*$";
    /**
     * 正则：负浮点数
     */
    public static final String REGEX_NEGATIVE_FLOAT = "^-[1-9]\\d*\\.\\d*|-0\\.\\d*[1-9]\\d*$";

    /************** If u want more please visit http://toutiao.com/i6231678548520731137/ **************/

    /**
     * 是否是只包含‘a-zA-Z’的字符窜
     *
     * @param txt
     * @return
     */
    public static boolean isOnlyChar(String txt) {
        return txt.matches("[a-zA-Z]+");
    }

    /**
     * 是否是只包含中文的字符窜
     *
     * @param txt
     * @return
     */
    public static boolean isOnlyChineseChar(String txt) {
        return txt.matches("[一-龥]+");
    }

    /**
     * 是否是只包含‘A-Z’的大写字符窜
     *
     * @param txt
     * @return
     */
    public static boolean isOnlyLargeChar(String txt) {
        return txt.matches("[A-Z]+");
    }

    /**
     * 是否是只包含‘a-z’的小写字符窜
     *
     * @param txt
     * @return
     */
    public static boolean isOnlyLowerChar(String txt) {
        return txt.matches("[a-z]+");
    }

    /**
     * 是否是只包含‘0-9’的数字字符窜
     *
     * @param txt
     * @return
     */
    public static boolean isOnlyNumber(String txt) {
        return txt.matches("[0-9]+");
    }

    /**
     * 是否是只包含数字和下划线的字符窜
     *
     * @param txt
     * @return
     */
    public static boolean isOnlyNumberCharAndUnderline(String txt) {
        return txt.matches("\\w+");
    }

    /**
     * 是否是只包含数字和‘a-zA-Z’的字符窜
     *
     * @param txt
     * @return
     */
    public static boolean isOnlyNumerAndChar(String txt) {
        return txt.matches("[A-Za-z0-9]+");
    }

    /**
     * 验证手机号
     *
     * @param mobile 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isMobile(String mobile) {
        return isMatch(REGEX_MOBILE, mobile);
    }

    /**
     * 验证电话号码
     *
     * @param tel 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isTel(String tel) {
        return isMatch(REGEX_TEL, tel);
    }

    /**
     * 验证邮箱
     *
     * @param email 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isEmail(String email) {
        return isMatch(REGEX_EMAIL, email);
    }

    /**
     * 验证URL
     *
     * @param url 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isURL(String url) {
        return isMatch(REGEX_URL, url);
    }

    /**
     * 验证汉字
     *
     * @param zh 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isZh(String zh) {
        return isMatch(REGEX_ZH, zh);
    }

    /**
     * 验证用户名
     * <p>取值范围为a-z,A-Z,0-9,"_",汉字，不能以"_"结尾,用户名必须是6-20位</p>
     *
     * @param username 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isUsername(String username) {
        return isMatch(REGEX_USERNAME, username);
    }

    /**
     * 验证yyyy-MM-dd格式的日期校验，已考虑平闰年
     *
     * @param date 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isDate(String date) {
        return isMatch(REGEX_DATE, date);
    }

    /**
     * 验证IP地址
     *
     * @param ip 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isIP(String ip) {
        return isMatch(REGEX_IP, ip);
    }

    /**
     * 判断是否匹配正则
     *
     * @param regex 正则表达式
     * @param str   要匹配的字符串
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isMatch(String regex, String str) {
        return str != null && str.length() > 0 && Pattern.matches(regex, str);
    }

    /**
     * 验证身份证号码15位
     *
     * @param idcard 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isIDCard15(String idcard) {
        return isMatch(REGEX_ID_CARD15, idcard);
    }

    /**
     * 验证身份证号码18位
     *
     * @param idcard 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isIDCard18(String idcard) {
        return isMatch(REGEX_ID_CARD18, idcard);
    }

    private RegexUtils() {
    }

    /**
     * 功能：身份证的有效验证
     *
     * @param idCard 身份证号
     * @return true 有效：false 无效
     * @throws ParseException
     */
    public static boolean isIDCard(String idCard) {
        if (idCard == null || idCard.length() != 18) {
            return false;
        }
        String[] valCodeArr = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
        String[] wi = {"7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7", "9", "10", "5", "8", "4", "2"};
        // ================ 号码的长度18位 ================
        String ai = idCard.substring(0, 17);
        // ================ 数字 除最后一位都为数字 ================
        if (!isOnlyNumber(ai)) {
            return false;
        }
        // ================ 出生年月是否有效 ================
        String strYear = ai.substring(6, 10);// 年份
        String strMonth = ai.substring(10, 12);// 月份
        String strDay = ai.substring(12, 14);// 日

        if (!isDate(strYear + "-" + strMonth + "-" + strDay)) {
            //errorInfo = "身份证生日无效。";
            return false;
        }

        if (Integer.parseInt(strDay) > 31 || Integer.parseInt(strDay) == 0) {
            //errorInfo = "身份证日期无效";
            return false;
        }

        if (Integer.parseInt(strMonth) > 12 || Integer.parseInt(strMonth) == 0) {
            //errorInfo = "身份证月份无效";
            return false;
        }

        GregorianCalendar gc = new GregorianCalendar();
        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");
        try {
            if ((gc.get(Calendar.YEAR) - Integer.parseInt(strYear)) > 150 || (gc.getTime().getTime() - s.parse(strYear + "-" + strMonth + "-" + strDay).getTime()) < 0) {
                //errorInfo = "身份证生日不在有效范围。";
                return false;
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }

        // ================ 地区码时候有效 ================
        Hashtable codeTable = getIDCardAreaCode();
        if (codeTable.get(ai.substring(0, 2)) == null) {
            //errorInfo = "身份证地区编码错误。";
            return false;
        }
        // ================ 判断最后一位的值 ================
        int totalMulAiWi = 0;
        for (int i = 0; i < 17; i++) {
            totalMulAiWi = totalMulAiWi + Integer.parseInt(String.valueOf(ai.charAt(i))) * Integer.parseInt(wi[i]);
        }
        int modValue = totalMulAiWi % 11;
        String verifyCode = valCodeArr[modValue];
        ai = ai + verifyCode;

        //errorInfo = "身份证无效，不是合法的身份证号码";
        return ai.equals(idCard);
    }

    /**
     * 功能：设置地区编码
     *
     * @return Hashtable 对象
     */
    public static Hashtable getIDCardAreaCode() {
        Hashtable hashtable = new Hashtable();
        hashtable.put("11", "北京");
        hashtable.put("12", "天津");
        hashtable.put("13", "河北");
        hashtable.put("14", "山西");
        hashtable.put("15", "内蒙古");
        hashtable.put("21", "辽宁");
        hashtable.put("22", "吉林");
        hashtable.put("23", "黑龙江");
        hashtable.put("31", "上海");
        hashtable.put("32", "江苏");
        hashtable.put("33", "浙江");
        hashtable.put("34", "安徽");
        hashtable.put("35", "福建");
        hashtable.put("36", "江西");
        hashtable.put("37", "山东");
        hashtable.put("41", "河南");
        hashtable.put("42", "湖北");
        hashtable.put("43", "湖南");
        hashtable.put("44", "广东");
        hashtable.put("45", "广西");
        hashtable.put("46", "海南");
        hashtable.put("50", "重庆");
        hashtable.put("51", "四川");
        hashtable.put("52", "贵州");
        hashtable.put("53", "云南");
        hashtable.put("54", "西藏");
        hashtable.put("61", "陕西");
        hashtable.put("62", "甘肃");
        hashtable.put("63", "青海");
        hashtable.put("64", "宁夏");
        hashtable.put("65", "新疆");
        hashtable.put("71", "台湾");
        hashtable.put("81", "香港");
        hashtable.put("82", "澳门");
        hashtable.put("91", "国外");
        return hashtable;
    }
}
